package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import org.jsola.common.ArrayKit;
import org.jsola.common.DateKit8;
import org.jsola.common.StrKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.constant.CalculatePayConstant;
import org.jsola.hr.entity.EmpInfoDO;
import org.jsola.hr.entity.salary.HourAmountRule;
import org.jsola.hr.entity.salary.LeaveOfficeRule;
import org.jsola.hr.entity.salary.RedPacketRule;
import org.jsola.hr.query.CalculatePayQuery;
import org.jsola.hr.service.ICalculatePayService;
import org.jsola.hr.service.IEmpInfoService;
import org.jsola.hr.service.ISalaryCalculateRuleService;
import org.jsola.hr.vo.CalculatePayVO;
import org.jsola.hr.vo.SalaryCalculateRuleVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wu
 */
@Service("hrCalculatePayServiceImpl")
public class CalculatePayServiceImpl implements ICalculatePayService {

    @Autowired
    private ISalaryCalculateRuleService salaryCalculateRuleService;
    @Autowired
    private IEmpInfoService empInfoService;

    @Override
    public CalculatePayVO calculatePay(CalculatePayQuery calculatePayQuery, String siteId) {

        // 根据员工id查询员工信息
        EmpInfoDO empInfoDO = empInfoService.selectDOById(calculatePayQuery.getEmpInfoId(), siteId);
        ParamException.notNull(empInfoDO, "员工信息不存在或已删除，请稍后重试或联系管理员");

        // 查询参数
        BigDecimal manHour = calculatePayQuery.getManHour();
        BigDecimal manDay = calculatePayQuery.getManDay();
//        ParamException.isFalse(manHour.intValue() > 744, "工时不能超过744个小时");
        String month = calculatePayQuery.getThisStartMonth() < 10 ?
                "0" + calculatePayQuery.getThisStartMonth() : calculatePayQuery.getThisStartMonth() + "";
        String thisStartTime = calculatePayQuery.getThisStartYear() + "-" + month + "-01";
        BigDecimal historyManHour = calculatePayQuery.getHistoryManHour();
        ParamException.notNull(historyManHour, "历史工时不能为空");
        String entryTime = DateKit8.format(empInfoDO.getJoinDate(),"yyyy-MM-dd");
        ParamException.notNull(entryTime, "入职时间不能为空");
        String leaveOfficeTime = DateKit8.format(empInfoDO.getLeaveDate(),"yyyy-MM-dd");
        BigDecimal historyDay = calculatePayQuery.getHistoryDay() == null ?
                new BigDecimal("0") : calculatePayQuery.getHistoryDay();
//        ParamException.notNull(historyDay, "历史出勤天数不能为空");
        BigDecimal workerSalary = calculatePayQuery.getWorkerSalary();

        CalculatePayVO calculatePayVO = new CalculatePayVO();
        calculatePayVO.setAmount(new BigDecimal("0"));
        calculatePayVO.setServiceFee(new BigDecimal("0"));
        calculatePayVO.setSpecialFee(new BigDecimal("0"));
        calculatePayVO.setServiceFeeSpecialFee(new BigDecimal("0"));
        calculatePayVO.setServiceFeeWorkTimeRake(new BigDecimal("0"));
        calculatePayVO.setWorkTimeRake(new BigDecimal("0"));
        // 计算薪资
        if (StrKit.isNotEmpty(empInfoDO.getSalaryRuleId())) {
            calculatePay(calculatePayVO, empInfoDO.getSalaryRuleId(), siteId,
                    manHour, manDay, thisStartTime, historyManHour, historyDay, entryTime,
                    leaveOfficeTime, calculatePayQuery.getIsAmountRedPacket(), null);
        }
        // 计算劳务费
        if (StrKit.isNotEmpty(empInfoDO.getServiceFeeRuleId())) {
            calculatePay(calculatePayVO, empInfoDO.getServiceFeeRuleId(), siteId,
                    manHour, manDay, thisStartTime, historyManHour, historyDay, entryTime,
                    leaveOfficeTime, calculatePayQuery.getIsServiceFeeRedPacket(), workerSalary);
        }
        // 一次性费用
        calculatePayVO.setOneTimeFee(calculatePayVO.getAmount()
                .subtract(calculatePayVO.getSpecialFee().add(calculatePayVO.getWorkTimeRake())));
        calculatePayVO.setServiceFeeOneTimeFee(calculatePayVO.getServiceFee()
                .subtract(calculatePayVO.getServiceFeeSpecialFee().add(calculatePayVO.getServiceFeeWorkTimeRake())));
        return calculatePayVO;
    }

    @Override
    public CalculatePayVO calculatePay(String empInfoId, Integer manHour, Integer manDay, Integer year, Integer month,
                                       Integer historyManHour, Integer historyDay, String isAmountRedPacket,
                                       String isServiceFeeRedPacket, BigDecimal workerSalary, String siteId) {
        CalculatePayQuery calculatePayQuery = new CalculatePayQuery();
        ParamException.isFalse(StrKit.isEmpty(empInfoId), "员工id不能为空");
        ParamException.isFalse(Objects.isNull(manHour), "工时不能为空");
        ParamException.isFalse(Objects.isNull(manDay), "出勤天数不能为空");
        ParamException.isFalse(Objects.isNull(year), "本次开始时间(年)不能为空");
        ParamException.isFalse(Objects.isNull(month), "本次开始时间（月）不能为空");
        ParamException.isFalse(Objects.isNull(historyManHour), "历史工时不能为空");
        ParamException.isFalse(Objects.isNull(historyDay), "历史出勤天数不能为空");
        calculatePayQuery.setEmpInfoId(empInfoId);
        calculatePayQuery.setManHour(new BigDecimal(manHour + ""));
        calculatePayQuery.setManDay(new BigDecimal(manDay + ""));
        calculatePayQuery.setThisStartYear(year);
        calculatePayQuery.setThisStartMonth(month);
        calculatePayQuery.setHistoryManHour(new BigDecimal(historyManHour + ""));
        calculatePayQuery.setHistoryDay(new BigDecimal(historyDay + ""));
        calculatePayQuery.setIsAmountRedPacket(isAmountRedPacket);
        calculatePayQuery.setIsServiceFeeRedPacket(isServiceFeeRedPacket);
        calculatePayQuery.setWorkerSalary(workerSalary);
        return calculatePay(calculatePayQuery, siteId);
    }

    @Override
    public CalculatePayVO calculatePay(String empInfoId, BigDecimal manHour, BigDecimal manDay, Integer year, Integer month, BigDecimal historyManHour, BigDecimal historyDay, String isAmountRedPacket, String isServiceFeeRedPacket, BigDecimal workerSalary, String siteId) {
        CalculatePayQuery calculatePayQuery = new CalculatePayQuery();
        ParamException.isFalse(StrKit.isEmpty(empInfoId), "员工id不能为空");
        ParamException.isFalse(Objects.isNull(manHour), "工时不能为空");
        ParamException.isFalse(Objects.isNull(manDay), "出勤天数不能为空");
        ParamException.isFalse(Objects.isNull(year), "本次开始时间(年)不能为空");
        ParamException.isFalse(Objects.isNull(month), "本次开始时间（月）不能为空");
        ParamException.isFalse(Objects.isNull(historyManHour), "历史工时不能为空");
        ParamException.isFalse(Objects.isNull(historyDay), "历史出勤天数不能为空");
        calculatePayQuery.setEmpInfoId(empInfoId);
        calculatePayQuery.setManHour(manHour);
        calculatePayQuery.setManDay(manDay);
        calculatePayQuery.setThisStartYear(year);
        calculatePayQuery.setThisStartMonth(month);
        calculatePayQuery.setHistoryManHour(historyManHour);
        calculatePayQuery.setHistoryDay(historyDay);
        calculatePayQuery.setIsAmountRedPacket(isAmountRedPacket);
        calculatePayQuery.setIsServiceFeeRedPacket(isServiceFeeRedPacket);
        calculatePayQuery.setWorkerSalary(workerSalary);
        return calculatePay(calculatePayQuery, siteId);
    }

    /**
     * 根据规则id计算薪资或劳务费
     * @param calculatePayVO 返回的参数
     * @param salaryCalculateRuleId 规则id
     * @param siteId 站点id
     * @param manHour 工时
     * @param thisStartTime 本次开始时间
     * @param historyManHour 历史工时
     * @param historyDay 历史出勤天数
     * @param entryTime 入职日期
     * @param leaveOfficeTime 离职日期
     * @param isRedPacket 是否发放过红包：格式：1110011000（1表示红包已发放，无需再次判断是否发放）
     * @param workerSalary 工人工资（计算劳务费抽成使用）
     */
    private void calculatePay(CalculatePayVO calculatePayVO, String salaryCalculateRuleId, String siteId,
                              BigDecimal manHour, BigDecimal manDay, String thisStartTime, BigDecimal historyManHour, BigDecimal historyDay,
                              String entryTime, String leaveOfficeTime, String isRedPacket, BigDecimal workerSalary) {
        // 查询计算规则
        SalaryCalculateRuleVO salaryCalculateRuleVO = salaryCalculateRuleService.selectById(salaryCalculateRuleId, siteId);
        ParamException.notNull(salaryCalculateRuleVO, "没有找到id为：" + salaryCalculateRuleId + "对应的规则，请联系管理员!");
        if (salaryCalculateRuleVO.getRuleType().equals(CalculatePayConstant.RULE_TYPE_COMMISSION)) {
            calculatePayVO.setServiceFeeName(salaryCalculateRuleVO.getName());
        } else {
            calculatePayVO.setAmountName(salaryCalculateRuleVO.getName());
        }
        if (salaryCalculateRuleVO.getRuleType().equals(CalculatePayConstant.RULE_TYPE_EQUALLY)) {
            return;
        }
        BigDecimal number = manHour;
        // 判断记天还是计时
        if (CalculatePayConstant.SHOW_TYPE_MAN_DAY.equals(salaryCalculateRuleVO.getShowType())) {
            number = manDay;
            historyManHour = historyDay;
        }
        // 工价
        Long hourAmount = salaryCalculateRuleVO.getHourAmount() == null ? 0 : salaryCalculateRuleVO.getHourAmount();
        // 根据工价规则计算后的薪资
        Long amount = number.multiply(new BigDecimal(hourAmount + "")).longValue();
        amount = calculatePayByHourAmountRule(hourAmount, amount, salaryCalculateRuleVO,
                thisStartTime, number, historyManHour, historyDay, entryTime);

        if (salaryCalculateRuleVO.getRuleType().equals(CalculatePayConstant.RULE_TYPE_MAN_HOUR)) {
            calculatePayVO.setWorkTimeRake(BigDecimal.valueOf(amount.doubleValue() / 10000));
        } else {
            calculatePayVO.setServiceFeeWorkTimeRake(BigDecimal.valueOf(amount.doubleValue() / 10000));
            // 计算劳务费抽成
            BigDecimal commissionRatio = salaryCalculateRuleVO.getCommissionRatio();
            if (workerSalary != null && !workerSalary.equals(new BigDecimal(0)) && commissionRatio != null) {
                BigDecimal serviceFeeWork = workerSalary.multiply(commissionRatio).
                        divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
                calculatePayVO.setServiceFeeWorkTimeRake(serviceFeeWork);
                amount = serviceFeeWork.multiply(new BigDecimal("10000")).longValue();
            }
        }
        // 根据离职规则计算后的薪资
        if (StrKit.isNotEmpty(leaveOfficeTime)) {
            amount = calculatePayByLeaveOfficeRule(amount, salaryCalculateRuleVO, number,
                    historyManHour, entryTime, leaveOfficeTime);
        }

        // 根据红包规则计算后的薪资
        amount = calculatePayByRedPacketRule(calculatePayVO, amount, salaryCalculateRuleVO, thisStartTime,
                number, historyManHour, entryTime, isRedPacket);

        // 特殊费用
        if (salaryCalculateRuleVO.getSpecialCost() != null && salaryCalculateRuleVO.getSpecialCost() != 0) {
            if (salaryCalculateRuleVO.getRuleType().equals(CalculatePayConstant.RULE_TYPE_MAN_HOUR)) {
                calculatePayVO.setSpecialFee(BigDecimal.valueOf(salaryCalculateRuleVO.getSpecialCost().doubleValue() / 10000));
            } else {
                calculatePayVO.setServiceFeeSpecialFee(BigDecimal.valueOf(salaryCalculateRuleVO.getSpecialCost().doubleValue() / 10000));
            }
            amount += salaryCalculateRuleVO.getSpecialCost() * 100;
        }
        // 返回费用(薪资或劳务费)
        if (CalculatePayConstant.RULE_TYPE_MAN_HOUR.equals(salaryCalculateRuleVO.getRuleType())) {
            calculatePayVO.setAmount(BigDecimal.valueOf(amount.doubleValue()/10000));
        } else {
            calculatePayVO.setServiceFee(BigDecimal.valueOf(amount.doubleValue()/10000));
        }
    }

    /**
     * 根据工价规则计算薪资
     * @param oldHourAmount 初始工价
     * @param amount 初始薪资
     * @param salaryCalculateRuleVO 规则
     * @param thisStartTime 本次开始时间
     * @param manHour 工时
     * @param historyManHour 历史工时
     * @param entryTime 入职时间
     * @return 经过工价规则洗礼的薪资
     */
    private Long calculatePayByHourAmountRule(Long oldHourAmount, Long amount, SalaryCalculateRuleVO salaryCalculateRuleVO, String thisStartTime,
                                              BigDecimal manHour, BigDecimal historyManHour, BigDecimal historyDay, String entryTime) {
        // 记录初始薪资
        long recordAmount = amount;
        amount = 0L;
        // 根据工价更改规则计算薪资
        String hourAmountRuleStr = salaryCalculateRuleVO.getHourAmountRule();
        // 构建工价规则List
        List<HourAmountRule> hourAmountRuleList = new ArrayList<>();
        if (StrKit.isNotEmpty(hourAmountRuleStr)) {
            hourAmountRuleList = JSON.parseArray(hourAmountRuleStr, HourAmountRule.class);
        }
        // 按照变更规则类型分组   1：固定日期变更工价，2：按月/天/工时变更工价
        Map<Integer, List<HourAmountRule>> hourAmountRuleMap =
                hourAmountRuleList.stream().collect(Collectors.groupingBy(HourAmountRule::getType));
        // 取出固定日期变更工价的分组，按日期排序，从里面判断符合的条件，多个条件符合取日期最后一个条件
        List<HourAmountRule> dateHourAmount = hourAmountRuleMap.get(CalculatePayConstant.HOUR_RULE_TYPE_FIXED_DATE);
        if (Objects.nonNull(dateHourAmount)) {
            dateHourAmount = dateHourAmount.stream().sorted(Comparator.comparing(HourAmountRule::getTime)).collect(Collectors.toList());
            // 薪资
            long temporaryAmount = 0L;
            for (HourAmountRule hourAmountRule : dateHourAmount) {
                if (DateKit8.parseDate(thisStartTime).getTime() >= DateKit8.parseDate(hourAmountRule.getTime()).getTime()) {
                    // 固定时间更改工价
                    temporaryAmount = manHour.multiply(new BigDecimal("" + hourAmountRule.getNewHourAmount()))
                            .multiply(new BigDecimal("10000")).longValue();
                }
            }
            amount = temporaryAmount;
        }
        // 取出按单位变更工价的分组
        List<HourAmountRule> unitHourAmount = hourAmountRuleMap.get(CalculatePayConstant.HOUR_RULE_TYPE_UNIT);
        if (Objects.nonNull(unitHourAmount)) {
            amount = calculateAmountByUnitHourAmount(oldHourAmount, unitHourAmount, entryTime,
                    thisStartTime, manHour, amount, historyManHour, historyDay, recordAmount);
        }
        return amount == 0 ? recordAmount : amount;
    }

    /**
     * 根据按单位变更工价的分组计算薪资
     * @param unitHourAmount 工价规则
     * @param entryTime 入职时间
     * @param thisStartTime 本次开始时间
     * @param manHour 工时
     * @param amount 薪资
     * @param historyManHour 历史工时
     * @param historyDay 历史出勤天数
     * @param recordAmount 初始薪资
     * @return 计算后的薪资
     */
    private Long calculateAmountByUnitHourAmount(Long oldHourAmount, List<HourAmountRule> unitHourAmount, String entryTime,
                                                 String thisStartTime, BigDecimal manHour, Long amount,
                                                 BigDecimal historyManHour, BigDecimal historyDay, Long recordAmount) {
        // 按照单位类型分组 1-月，2-天，3-工时
        Map<Integer, List<HourAmountRule>> hourAmountRuleUnitMap =
                unitHourAmount.stream().collect(Collectors.groupingBy(HourAmountRule::getUnits));
        // 取出月份的分组 按大小排序
        List<HourAmountRule> monthHourAmount = hourAmountRuleUnitMap.get(CalculatePayConstant.UNIT_MONTH);
        if (Objects.nonNull(monthHourAmount)) {
            monthHourAmount = monthHourAmount.stream().sorted(Comparator.comparing(HourAmountRule::getNumber)).collect(Collectors.toList());
            // 薪资
            long temporaryAmount = 0L;
            for (HourAmountRule hourAmountRule : monthHourAmount) {
                // 满月数更改工价
                Date entryDate = DateKit8.parseDate(entryTime);
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(entryDate);
                calendar.add(Calendar.MONTH, hourAmountRule.getNumber().intValue());
                if (calendar.getTimeInMillis() <= DateKit8.parseDate(thisStartTime).getTime()) {
                    temporaryAmount = manHour.multiply(new BigDecimal("" + hourAmountRule.getNewHourAmount()))
                            .multiply(new BigDecimal("10000")).longValue();
                }
            }
            amount = amount == 0 ? temporaryAmount : Math.max(amount, temporaryAmount);
        }
        // 取出天数的分组 按大小排序
        List<HourAmountRule> dayHourAmount = hourAmountRuleUnitMap.get(CalculatePayConstant.UNIT_DAY);
        if (Objects.nonNull(dayHourAmount)) {
            dayHourAmount = dayHourAmount.stream().sorted(Comparator.comparing(HourAmountRule::getNumber)).collect(Collectors.toList());
            // 薪资
            long temporaryAmount = 0L;
            for (HourAmountRule hourAmountRule : dayHourAmount) {
                if (historyDay.compareTo(hourAmountRule.getNumber()) >= 0) {
                    temporaryAmount = manHour.multiply(new BigDecimal("" + hourAmountRule.getNewHourAmount()))
                            .multiply(new BigDecimal("10000")).longValue();
                }
            }
            amount = amount == 0 ? temporaryAmount : Math.max(amount, temporaryAmount);
        }
        // 取出工时的分组 按大小排序
        List<HourAmountRule> hourHourAmount = hourAmountRuleUnitMap.get(CalculatePayConstant.UNIT_HOUR);
        if (Objects.nonNull(hourHourAmount)) {
            hourHourAmount = hourHourAmount.stream().sorted(Comparator.comparing(HourAmountRule::getNumber))
                    .collect(Collectors.toList());
            // 薪资
            long temporaryAmount;
            // 计算每一种条件增加的金额
            List<Long> longList = new ArrayList<>(hourHourAmount.size());
            for (int i = 0 ; i < hourHourAmount.size() ; i++) {
                if (historyManHour.add(manHour).compareTo(hourHourAmount.get(i).getNumber()) < 0) {
                    break;
                }
                if (i + 1 < hourHourAmount.size()) {
                    if (historyManHour.compareTo(hourHourAmount.get(i + 1).getNumber()) < 0) {
                        if (historyManHour.compareTo(hourHourAmount.get(i).getNumber()) >= 0) {
                            BigDecimal cha = historyManHour.subtract(hourHourAmount.get(i).getNumber());
                            BigDecimal newHour = hourHourAmount.get(i + 1).getNumber()
                                    .subtract(hourHourAmount.get(i).getNumber())
                                    .subtract(cha);
                            BigDecimal chaHourAmount = new BigDecimal(Math.round(hourHourAmount.get(i).getNewHourAmount() * 10000) - oldHourAmount + "");
                            if (historyManHour.add(manHour).compareTo(hourHourAmount.get(i + 1).getNumber()) >= 0) {
                                long addLong = newHour.multiply(chaHourAmount).longValue();
                                longList.add(addLong);
                            } else {
                                longList.add(manHour.multiply(new BigDecimal("" + hourHourAmount.get(i).getNewHourAmount()))
                                        .multiply(new BigDecimal("10000")).longValue() - oldHourAmount);
                            }
                        } else {
                            if (historyManHour.add(manHour).compareTo(hourHourAmount.get(i + 1).getNumber()) >= 0) {
                                BigDecimal addLong = hourHourAmount.get(i + 1).getNumber().subtract(hourHourAmount.get(i).getNumber());
                                long newHourAmount = Math.round(hourHourAmount.get(i).getNewHourAmount() * 10000) - oldHourAmount;
                                addLong = addLong.multiply(new BigDecimal(newHourAmount + ""));
                                longList.add(addLong.longValue());
                            } else {
                                longList.add(manHour.add(historyManHour).subtract(hourHourAmount.get(i).getNumber())
                                        .multiply(new BigDecimal(Math.round(hourHourAmount.get(i).getNewHourAmount() * 10000) - oldHourAmount + ""))
                                        .longValue());
                            }
                        }
                    }
                } else {
                    if (historyManHour.compareTo(hourHourAmount.get(i).getNumber()) >= 0) {
                        longList.add(manHour.multiply(new BigDecimal(
                                Math.round(hourHourAmount.get(i).getNewHourAmount() * 10000) - oldHourAmount + ""))
                                .longValue());
                    } else {
                        longList.add(manHour.add(historyManHour).subtract(hourHourAmount.get(i).getNumber())
                                .multiply(new BigDecimal(Math.round(hourHourAmount.get(i).getNewHourAmount() * 10000) - oldHourAmount + ""))
                                .longValue());
                    }
                }
            }
            temporaryAmount = longList.stream().mapToLong(Long::longValue).sum();
            amount = amount == 0 ? temporaryAmount + recordAmount : Math.max(amount, temporaryAmount + recordAmount);
        }
        return amount;
    }





    /**
     * 根据离职规则计算薪资
     * @param amount 初始薪资
     * @param salaryCalculateRuleVO 规则
     * @param manHour 工时
     * @param historyManHour 历史工时
     * @param entryTime 入职时间
     * @param leaveOfficeTime 离职时间
     * @return 经过离职规则洗礼的薪资
     */
    private Long calculatePayByLeaveOfficeRule(Long amount, SalaryCalculateRuleVO salaryCalculateRuleVO, BigDecimal manHour,
                                               BigDecimal historyManHour, String entryTime, String leaveOfficeTime) {
        String leaveOfficeRuleStr = salaryCalculateRuleVO.getLeaveOfficeRule();
        // 构建离职规则List
        List<LeaveOfficeRule> leaveOfficeRuleList = new ArrayList<>();
        if (StrKit.isNotEmpty(leaveOfficeRuleStr)) {
            leaveOfficeRuleList = JSON.parseArray(leaveOfficeRuleStr, LeaveOfficeRule.class);
        }
        // 不扣除的比例
        double proportion;
        // 扣款金额
        long temporaryAmount = 0L;
        // 满足条件的离职规则
        List<LeaveOfficeRule> trueLeaveOfficeRuleList = new ArrayList<>();

        // 离职扣款规则类型：1：不满月/天/工时倒扣工资（按比例或者固定），2：不满月/天/工时减少工价
        Map<Integer, List<LeaveOfficeRule>> leaveOfficeRuleMap =
                leaveOfficeRuleList.stream().collect(Collectors.groupingBy(LeaveOfficeRule::getType));
        // 不满月/天/工时倒扣工资（按比例或者固定）
        List<LeaveOfficeRule> withholdLeaveOffice = leaveOfficeRuleMap.get(CalculatePayConstant.LEAVE_DEDUCT);
        buildTrueLeaveOfficeRuleList(withholdLeaveOffice, entryTime, leaveOfficeTime, trueLeaveOfficeRuleList, historyManHour.add(manHour));
        // 不满月/天/工时减少工价
        List<LeaveOfficeRule> reducePriceLeaveOffice = leaveOfficeRuleMap.get(CalculatePayConstant.LEAVE_DOWN_SALARY);
        buildTrueLeaveOfficeRuleList(reducePriceLeaveOffice, entryTime, leaveOfficeTime, trueLeaveOfficeRuleList, historyManHour.add(manHour));

        for (LeaveOfficeRule leaveOfficeRule : trueLeaveOfficeRuleList) {
            if (CalculatePayConstant.LEAVE_DEDUCT.equals(leaveOfficeRule.getType())) {
                // 倒扣工资
                if (CalculatePayConstant.RULE_TYPE_MAN_HOUR.equals(salaryCalculateRuleVO.getRuleType())) {
                    // 工价计算：直接扣除
                    temporaryAmount = Math.max(temporaryAmount, Math.round(leaveOfficeRule.getDeductAmount() * 10000));
                } else {
                    // 劳务费计算：按照比例
                    if (CalculatePayConstant.UNIT_MONTH.equals(leaveOfficeRule.getUnits())) {
                        // 月
                        Date entryDate = DateKit8.addMonths(DateKit8.parseDate(entryTime), leaveOfficeRule.getNumber().intValue());
                        // 实际工作月数
                        int months = getMonthDiff(entryDate, DateKit8.parseDate(leaveOfficeTime));
                        // 计算比例
                        proportion = BigDecimal.valueOf(months / leaveOfficeRule.getNumber().doubleValue())
                                .setScale(10, BigDecimal.ROUND_HALF_UP).doubleValue();
                    } else if (CalculatePayConstant.UNIT_DAY.equals(leaveOfficeRule.getUnits())) {
                        // 天
                        long days = DateKit8.getDaysBetween(DateKit8.parseDate(entryTime), DateKit8.parseDate(leaveOfficeTime));
                        // 计算比例
                        proportion = BigDecimal.valueOf(days / leaveOfficeRule.getNumber().doubleValue())
                                    .setScale(10, BigDecimal.ROUND_HALF_UP).doubleValue();
                    } else {
                        // 工时
                        // 计算比例
                        proportion = historyManHour.add(manHour)
                                .divide(new BigDecimal(leaveOfficeRule.getNumber() + ""), 10, BigDecimal.ROUND_HALF_UP)
                                .doubleValue();
                    }
                    double proportionAmount = leaveOfficeRule.getDeductAmount() * (1 - proportion);
                    proportionAmount = BigDecimal.valueOf(proportionAmount).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
                    temporaryAmount = Math.max(Math.round(proportionAmount * 10000), temporaryAmount);
                }
            } else {
                // 减少工价  差值就是扣款额（多条件取扣款最多的）
                long newAmount =  amount -
                        (manHour.multiply(new BigDecimal("" + leaveOfficeRule.getNewHourAmount()))
                                .multiply(new BigDecimal("10000")).longValue());
                temporaryAmount = Math.max(temporaryAmount, newAmount);
            }
        }
        return amount - temporaryAmount;
    }

    /**
     * 构建符合条件的离职规则List
     * @param leaveOfficeRuleList 离职扣款规则List
     * @param entryTime 入职时间
     * @param leaveOfficeTime 离职时间
     * @param trueLeaveOfficeRuleList 符合条件的list
     * @param sumHour 总工时
     */
    private void buildTrueLeaveOfficeRuleList(List<LeaveOfficeRule> leaveOfficeRuleList, String entryTime, String leaveOfficeTime,
                     List<LeaveOfficeRule> trueLeaveOfficeRuleList, BigDecimal sumHour) {
        if (Objects.nonNull(leaveOfficeRuleList)) {
            // 按照单位类型分组 1-月，2-天，3-工时
            Map<Integer, List<LeaveOfficeRule>> leaveOfficeRuleUnitMap =
                    leaveOfficeRuleList.stream().collect(Collectors.groupingBy(LeaveOfficeRule::getUnits));
            // 取出月份的分组 按大小排序
            List<LeaveOfficeRule> monthLeaveOffice = leaveOfficeRuleUnitMap.get(CalculatePayConstant.UNIT_MONTH);
            if (Objects.nonNull(monthLeaveOffice)) {
                monthLeaveOffice = monthLeaveOffice.stream().sorted(Comparator.comparing(LeaveOfficeRule::getNumber)).collect(Collectors.toList());
                for (LeaveOfficeRule leaveOfficeRule : monthLeaveOffice) {
                    Date entryDate = DateKit8.addMonths(DateKit8.parseDate(entryTime), leaveOfficeRule.getNumber().intValue());
                    if (entryDate.getTime() > DateKit8.parseDate(leaveOfficeTime).getTime()) {
                        trueLeaveOfficeRuleList.add(leaveOfficeRule);
                        break;
                    }
                }
            }
            // 取出天数的分组 按大小排序
            List<LeaveOfficeRule> dayLeaveOffice = leaveOfficeRuleUnitMap.get(CalculatePayConstant.UNIT_DAY);
            if (Objects.nonNull(dayLeaveOffice)) {
                dayLeaveOffice = dayLeaveOffice.stream().sorted(Comparator.comparing(LeaveOfficeRule::getNumber)).collect(Collectors.toList());
                for (LeaveOfficeRule leaveOfficeRule : dayLeaveOffice) {
                    long days = DateKit8.getDaysBetween(DateKit8.parseDate(entryTime), DateKit8.parseDate(leaveOfficeTime));
                    if (days < leaveOfficeRule.getNumber().intValue()) {
                        trueLeaveOfficeRuleList.add(leaveOfficeRule);
                        break;
                    }
                }
            }
            // 取出工时的分组 按大小排序
            List<LeaveOfficeRule> hourLeaveOffice = leaveOfficeRuleUnitMap.get(CalculatePayConstant.UNIT_HOUR);
            if (Objects.nonNull(hourLeaveOffice)) {
                hourLeaveOffice = hourLeaveOffice.stream().sorted(Comparator.comparing(LeaveOfficeRule::getNumber)).collect(Collectors.toList());
                for (LeaveOfficeRule leaveOfficeRule : hourLeaveOffice) {
                    // 不满工时
                    if (sumHour.compareTo(leaveOfficeRule.getNumber()) < 0) {
                        trueLeaveOfficeRuleList.add(leaveOfficeRule);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 获取两个日期相差的月数
     * @param d1  较大的日期
     * @param d2  较小的日期
     * @return 如果d1>d2返回 月数差 否则返回0
     */
    private int getMonthDiff(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(d1);
        c2.setTime(d2);
        if(c1.getTimeInMillis() < c2.getTimeInMillis()) {
            return 0;
        }
        int year1 = c1.get(Calendar.YEAR);
        int year2 = c2.get(Calendar.YEAR);
        int month1 = c1.get(Calendar.MONTH);
        int month2 = c2.get(Calendar.MONTH);
        int day1 = c1.get(Calendar.DAY_OF_MONTH);
        int day2 = c2.get(Calendar.DAY_OF_MONTH);
        // 获取年的差值 假设 d1 = 2015-8-16 d2 = 2011-9-30
        int yearInterval = year1 - year2;
        // 如果 d1的 月-日 小于 d2的 月-日 那么 yearInterval-- 这样就得到了相差的年数
        boolean yearDown = month1 < month2 || month1 == month2 && day1 < day2;
        if(yearDown) {
            yearInterval --;
        }
        // 获取月数差值
        int monthInterval = (month1 + 12) - month2 ;
        if(day1 < day2) {
            monthInterval --;
        }
        monthInterval %= 12;
        return yearInterval * 12 + monthInterval;
    }





    /**
     * 根据红包规则计算薪资
     * @param calculatePayVO 薪资对象
     * @param amount 初始薪资
     * @param salaryCalculateRuleVO 规则
     * @param thisStartTime 本次开始时间
     * @param manHour 工时
     * @param historyManHour 历史工时
     * @param entryTime 入职时间
     * @param isRedPacket 是否发放过红包：格式：1110011000（1表示红包已发放，无需再次判断是否发放）
     * @return 经过红包规则洗礼的薪资
     */
    private Long calculatePayByRedPacketRule(CalculatePayVO calculatePayVO, Long amount,
                                             SalaryCalculateRuleVO salaryCalculateRuleVO, String thisStartTime,
                                             BigDecimal manHour, BigDecimal historyManHour, String entryTime,
                                             String isRedPacket) {
        // 本次结束时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateKit8.parseDate(thisStartTime));
        // 月份加一
        calendar.add(Calendar.MONTH, 1);
        // 天设置为1
        calendar.set(Calendar.DATE, 1);
        // 时
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        // 分
        calendar.set(Calendar.MINUTE, 0);
        // 秒
        calendar.set(Calendar.SECOND, 0);
        // 毫秒
        calendar.set(Calendar.MILLISECOND, 0);
        if (DateKit8.parseDate(entryTime).getTime() > calendar.getTimeInMillis()) {
            // 入职时间大于本次结束时间：不计算红包规则
            return amount;
        }
        String redPacketRuleStr = salaryCalculateRuleVO.getRedPacketRule();
        // 构建红包规则List
        List<RedPacketRule> redPacketRuleList = new ArrayList<>();
        if (StrKit.isNotEmpty(redPacketRuleStr)) {
            redPacketRuleList = JSON.parseArray(redPacketRuleStr, RedPacketRule.class);
        }
        List<String> isRed = ArrayKit.split(isRedPacket, "");
        if (StrKit.isEmpty(isRedPacket)) {
            isRed = new ArrayList<>();
        }

        for (int i = isRed.size(); i < redPacketRuleList.size(); i++) {
            isRed.add(CalculatePayConstant.RED_PACKET_NO);
        }
        for (int i = 0; i < redPacketRuleList.size(); i++) {
            RedPacketRule redPacketRule = redPacketRuleList.get(i);
            if (CalculatePayConstant.RED_PACKET_YES.equals(isRed.get(i))) {
                continue;
            }
            boolean send = false;
            if (CalculatePayConstant.UNIT_HOUR.equals(redPacketRule.getUnits())) {
                // 满工时发放
                if (historyManHour.add(manHour).compareTo(redPacketRule.getNumber()) >= 0) {
                    send = true;
                }
            } else if (CalculatePayConstant.UNIT_DAY.equals(redPacketRule.getUnits())
                    && DateKit8.getDaysBetween(DateKit8.parseDate(entryTime), calendar.getTime()) >= redPacketRule.getNumber().intValue()) {
                // 满天数发放
                send = true;
            } else if (CalculatePayConstant.UNIT_MONTH.equals(redPacketRule.getUnits())) {
                // 满月数发放
                Date entryDate = DateKit8.parseDate(entryTime);
                Calendar calendarMonth = Calendar.getInstance();
                calendarMonth.setTime(entryDate);
                calendarMonth.add(Calendar.MONTH, redPacketRule.getNumber().intValue());
                // 时
                calendarMonth.set(Calendar.HOUR_OF_DAY, 0);
                // 分
                calendarMonth.set(Calendar.MINUTE, 0);
                // 秒
                calendarMonth.set(Calendar.SECOND, 0);
                // 毫秒
                calendarMonth.set(Calendar.MILLISECOND, 0);
                if (calendarMonth.getTimeInMillis() <= calendar.getTime().getTime()) {
                    send = true;
                }
            }
            if (send) {
                amount += Math.round(redPacketRule.getRedPacketAmount() * 10000);
                isRed.set(i, CalculatePayConstant.RED_PACKET_YES);
            }
        }
        StringBuilder isRedPacketSb = new StringBuilder();
        for (String s : isRed) {
            isRedPacketSb.append(s);
        }
        if (CalculatePayConstant.RULE_TYPE_MAN_HOUR.equals(salaryCalculateRuleVO.getRuleType())) {
            calculatePayVO.setIsAmountRedPacket(isRedPacketSb.toString());
        } else {
            calculatePayVO.setIsServiceFeeRedPacket(isRedPacketSb.toString());
        }

        return amount;
    }




}
